/******************************************************************************
 * Copyright (C) 2024 Createtree, Inc.(Gmbh) or its affiliates.
 *
 * All Rights Reserved.
 *
 * @file bsp_led_driver.c
 *
 * @par dependencies
 * - bsp_led_driver.h
 * - stdint.h
 *
 * @author Createtree | R&D Dept. | liuzhihuawy@163.com
 *
 * @brief Provide the HAL APIs of LED and corresponding opetions.
 *
 * Processing flow:
 *
 * call directly.
 *
 * @version V1.0 2024-10-24
 *
 * @note 1 tab == 4 spaces!
 *
 *****************************************************************************/

//***************************** Includes begin ******************************//
#include "bsp_led_driver.h"
//***************************** Includes end ********************************//

//***************************** Defines begin *******************************//

#ifdef DEBUG
/* User provide the LED_LOG at other files */
extern int LED_LOG(const char * fmt, ...);
#else
#define LED_LOG(fmt, ...)
#endif /* DEBUG */

#ifdef USING_LED_ASSERT
#ifdef DEBUG
void LED_ASSERT(int LineNum, const char * FuncName)
{
    LED_LOG("Assert failed in line:%d,func:%s\r\n", LineNum, FuncName);
    while (1)
    {
        ;
    }
}
#define ASSERT(condition) if(!(condition)){LED_ASSERT(__LINE__, __func__);}
#else 
#error "USING_LED_ASSERT is defined, but DEBUG is not defined!"
#endif /* DEBUG */
#else
#define ASSERT(condition)
#endif /* USING_LED_ASSERT */


/**
 * @brief led driver init function
 * @param self 
 * @retval led_status_t 
 * @date 2024-10-25
 */
static led_status_t led_driver_init(bsp_led_driver_t * const self)
{
    ASSERT(NULL != self);
    LED_LOG("led driver init\r\n");
    led_status_t ret = self->p_led_opes_inst->pf_led_off();
    if (LED_STATUS_OK != ret)
    {
        LED_LOG("led driver init failed, ret:%d\r\n", ret);
    }
    return ret;
}

static led_status_t led_blink(bsp_led_driver_t * const self)
{
    led_status_t ret = LED_STATUS_OK;
/************** 1. Check the input parameters ************/
    ASSERT(NULL != self);
    LED_LOG("led blink start\r\n");

/*********** 2. Check the led driver is inited ***********/
    if (LED_DRIVER_INITED != self->is_inited)
    {
        ret = LED_STATUS_ERROR;
        LED_LOG("led driver is not inited\r\n");
        goto _exit_;
    }
    // TODO: use mutex to protect the led driver instance init
/************* 2. analyze the feature of led *************/
    do {
        uint32_t period       = self->period_ms;
        uint32_t blink_times  = self->blink_times;
        uint32_t duty_cycle   = self->duty_cycle;
        uint32_t time_trigger = (uint32_t)( period * duty_cycle / 100.0);
        LED_LOG("led blink analyze:\r\n");
        LED_LOG("period:%dms, blinkTimes:%d, duty:%d%%, timeTrig:%dms\r\n",
                period, blink_times, duty_cycle, time_trigger);
/*************** 3. run the operate of led ***************/
        for (size_t i = 0; i < blink_times; i++)
        {
                ret = self->p_led_opes_inst->pf_led_on();
                if (LED_STATUS_OK != ret)
                {
                    goto _exit_;
                }
                self->p_os_sleep->pf_sleep(time_trigger);
                ret = self->p_led_opes_inst->pf_led_off();
                if (LED_STATUS_OK != ret)
                {
                    goto _exit_;
                }
                self->p_os_sleep->pf_sleep(period - (time_trigger));
        }
    } while(0);
    _exit_:
    LED_LOG("led blink %s\r\n", LED_STATUS_OK == ret ? "OK":"ERROR");
    return ret;
}

/**
 * @brief led control function
 * @param self bsp_led_driver_t instance
 * @param period led blink period [ms]
 * @param blink_times led blink times [1-255]
 * @param duty_cycle  led duty cycle [led_duty_cycle_t]
 * @retval led_status_t 
 * @date 2024-10-26
 */
static led_status_t led_control(bsp_led_driver_t * const self,
                                const uint16_t period,
                                const uint8_t blink_times,
                                const led_duty_cycle_t duty_cycle)
{
    led_status_t ret = LED_STATUS_OK;
/************* 1. Check the input parameters *************/
    ASSERT(NULL != self);
    ASSERT(period > 0  && period < 10E3);
    ASSERT(blink_times > 0 && blink_times <= 255);
    ASSERT((int)duty_cycle >= LED_DUTY_CYCLE_MIN &&
           (int)duty_cycle <= LED_DUTY_CYCLE_MAX);
    LED_LOG("led control start\r\n");
    do {
/*********** 2. Check the led driver is inited ***********/
        if (LED_DRIVER_INITED != self->is_inited)
        {
            ret = LED_STATUS_ERROR;
            LED_LOG("led driver is not inited\r\n");
            break;
        }
/****** 3. add parameters to the led driver instance *****/
        self->period_ms   =      period;
        self->blink_times = blink_times;
        self->duty_cycle  =  duty_cycle;
/*************** 4. run the operate of led ***************/
        ret = led_blink(self);
    } while(0);
    LED_LOG("led control end, ret:%d\r\n", ret);
    return ret;
}

/**
 * 
 * @brief led constructor function
 * @param self led driver instance
 * @param hled_opes led operations instance
 * @param hos_sleep os sleep instance
 * @param htime_base time base instance
 * @retval led_status_t status of the operation
 * @note This function is used to initialize the led driver
 *  steps:
 *  1. Check the input parameters
 *  2. Check if the led driver is already initialized
 *  3. Add the interfaces to the led driver instance
 *  4. Initialize the led driver
 * @date 2024-10-25
 */
led_status_t bsp_led_constructor(
                                          bsp_led_driver_t * const        self,
                                    const led_operations_t * const   hled_opes,
#ifdef OS_SUPPORTING
                                    const os_sleep_t       * const   hos_sleep,
#endif /* OS_SUPPORTING */
                                    const time_base_t      * const htime_base)
{
    led_status_t ret = LED_STATUS_OK;
/************* 1. Check the input parameters *************/
    ASSERT(NULL != self && NULL != hled_opes && NULL != htime_base);
#ifdef OS_SUPPORTING
    ASSERT(NULL != hos_sleep->pf_sleep);
#endif /* OS_SUPPORTING */
    ASSERT( NULL != hled_opes->pf_led_on  &&
            NULL != hled_opes->pf_led_off &&
            NULL != htime_base->pf_get_time );
    do {
        LED_LOG("led driver constructor start\r\n");
/*** 2. Check if the led driver is already initialized ***/
        if (LED_DRIVER_INITED == self->is_inited)
        {
            ret = LED_STATUS_ERROR;
            LED_LOG("led driver is already initialized\r\n");
            break;
        }
/*** 3. Add the interfaces to the led driver instance ****/
        LED_LOG("add the interfaces to the led driver instance\r\n");
        self->p_time_base     =   htime_base;
        self->p_led_opes_inst =    hled_opes;
#ifdef OS_SUPPORTING
        self->p_os_sleep      =    hos_sleep;
#endif /* OS_SUPPORTING */
        // TODO: bind led control function to the led driver instance
        self->pf_led_ctrl = led_control;
/************* 4. Initialize the led driver **************/
        LED_LOG("initialize the led driver\r\n");
        self->period_ms   =                  0;
        self->blink_times =                  0;
        self->duty_cycle  = LED_DUTY_CYCLE_MIN;

        ret = led_driver_init(self);
        if (LED_STATUS_OK != ret)
        {
            self->p_led_opes_inst = NULL;
            self->p_time_base     = NULL;
#ifdef OS_SUPPORTING
            self->p_os_sleep      = NULL;
#endif
            LED_LOG("led driver init failed, ret:%d\r\n", ret);
            break;
        }
        self->is_inited = LED_DRIVER_INITED;
        LED_LOG("led driver constructor end\r\n");
    } while (0);
    return ret;
}

//******************************* Defines end *******************************//

//****************************** Declaring begin ****************************//

//****************************** Declaring end ******************************//
